简介
Buffer 是一个像 Array 的对象,主要用于操作字节,数组元素为 16 进制的两位数,即 0 到 255的数值,如果超出或者不足,小数等情况,会使用叠加,递减,省略小数部分的措施保证元素数值合法性,此外,采用 JavaScript 和 C++ 相结合的模式,将性能部分用 C++ 来实现,非性能相关的部分用 JavaScript 来实现
内存分配机制
Buffer 对象的内存分配不是在 V8 的堆内存中,而是在 Node 的 C++ 层面实现内存的申请,采用 slab 动态内存管理机制(先申请,后分配),以 4KB 作为界限来区分 Buffer 是大对象还是小对象,针对大对象,每次都 alloc 一个足够长的 SlowBuffer 对象(C++层)作为 slab 单元,针对小对象,则共用一个 slab,当不足以分配时(可分配小于 4KB),才会 alloc 一个新的 slab 内存进行再分配。需要注意的是当且仅当一个 slab 上面的所有小对象在作用域释放并都可以回收时,slab 的 8KB 才会被回收,此处会存在由于编码不当导致的内存泄漏,浪费问题
在 Buffer 中创建一个数组,需要注意以下规则:
- Buffer 是内存拷贝,而不是内存共享
- Buffer 占用内存被解释为一个数组,而不是字节数组。比如,new Uint32Array(new Buffer([1,2,3,4])) 创建了 4 个 Uint32Array,它的成员为 [1,2,3,4],而不是 [0x1020304] 或 [0x4030201]
1 | // 4KB 作为界限的由来:createPool 判断条件 |
四种 内存分配的 API
Buffer.from
Buffer.alloc
Buffer.allocUnSafe
Buffer.allocUnSafeSlow

支持与字符串相互转换
目前支持的字符串编码类型有:ASCII,UTF-8,UTF-16LE/UCS-2,Base64,Binary,Hex,可以用 isEncoding() 来判断是否支持编码
对于不支持的编码类型,可以通过 iconv
和 iconv-lite
两个模块来支持更多编码类型转换
转换成 Buffer:new Buffer(str,[encoding]) 和 buf.write(string,[offset],[length],[encoding])
转换成 String:buf.toString([encoding],[start],[end])
正确的拼接方式
用一个数组来存储接收到的所有 Buffer 片段并记录下所有片段的总长度,然后调用 Buffer.concat() 方法生成一个合并的 Buffer 对象
1 | Buffer.concat = function concat(list, length) { |
Buffer 与性能
网络传输用 Buffer 比直接传字符串要快很多
通过预先转换静态内容为 Buffer 对象,可以有效地减少 CPU 的重复使用,节省服务器资源。在构建 Web 应用中,可以选择将页面中的动态内容和静态内容分离,静态内容部分可以通过预先转换为 Buffer 的方式,是性能得到提升。由于文件自身是二进制数据,所以在不需要改变内容的场景下,尽量只读取 Buffer,然后直接传输,不做额外的转换,避免损耗
文件读取速度与 highWaterMark 有关
这个值代表了每次读取的长度,对 Buffer 内存的分配和使用有一定影响,设置过小,可能导致系统调用的次数过多,在读取一个相同的大文件时,该值越大,读取的速度越快